package com.hongtech.tiny.modules.sys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hongtech.tiny.modules.sys.dto.SysMenuNode;
import com.hongtech.tiny.modules.sys.entity.SysMenu;
import com.hongtech.tiny.modules.sys.mapper.SysMenuMapper;
import com.hongtech.tiny.modules.sys.service.SysMenuService;
import com.hongtech.tiny.modules.sys.vo.vo.MetaVo;
import com.hongtech.tiny.modules.sys.vo.vo.RouterVo;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 后台菜单管理Service实现类
 */
@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu>implements SysMenuService {

    @Override
    public boolean create(SysMenu sysMenu) {
        sysMenu.setCreateTime(new Date());
        updateLevel(sysMenu);
        return save(sysMenu);
    }

    /**
     * 修改菜单层级
     */
    private void updateLevel(SysMenu sysMenu) {
        if (sysMenu.getParentId() == 0) {
            //没有父菜单时为一级菜单
            sysMenu.setType(0);
        } else {
            //有父菜单时选择根据父菜单level设置
            SysMenu parentMenu = getById(sysMenu.getParentId());
            if (parentMenu != null) {
                sysMenu.setType(parentMenu.getType() + 1);
            } else {
                sysMenu.setType(0);
            }
        }
    }

    @Override
    public boolean update(Long id, SysMenu sysMenu) {
        sysMenu.setId(id);
        updateLevel(sysMenu);
        return updateById(sysMenu);
    }

    @Override
    public Page<SysMenu> list(Long parentId, Integer pageSize, Integer pageNum) {
        Page<SysMenu> page = new Page<>(pageNum,pageSize);
        QueryWrapper<SysMenu> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysMenu::getParentId,parentId).orderByAsc(SysMenu::getSort);
        return page(page,wrapper);
    }

    @Override
    public List<SysMenuNode> treeList() {
        List<SysMenu> menuList = list();
        return menuList.stream().filter(menu -> menu.getParentId().equals(0L))
                .map(menu -> covertMenuNode(menu, menuList)).collect(Collectors.toList());
    }

    @Override
    public boolean updateHidden(Long id, Integer hidden) {
        SysMenu sysMenu = new SysMenu();
        sysMenu.setId(id);
        sysMenu.setHidden(hidden);
        return updateById(sysMenu);
    }

    @Override
    public List<SysMenuNode> listMenuTreeByUserId(Long id) {
        List<SysMenu> menus = baseMapper.listMenuTreeByUserId(id);
        List<SysMenuNode> menuNodes = BeanUtil.copyToList(menus, SysMenuNode.class);
        return getChildPerms(menuNodes, 0);
    }

    @Override
    public List<RouterVo> buildMenus(List<SysMenuNode> menus) {
        List<RouterVo> routers = new LinkedList<>();
        for (SysMenuNode menu : menus) {
            RouterVo router = new RouterVo();
            router.setHidden(1 == menu.getHidden());
            router.setName(getRouteName(menu));
            router.setPath(getRouterPath(menu));
            router.setComponent(getComponent(menu));
            router.setMeta(new MetaVo(menu.getTitle(), menu.getIcon()));
            List<SysMenuNode> cMenus = menu.getChildren();
            if (CollectionUtils.isNotEmpty(cMenus) && menu.getType() == 0) {
                router.setAlwaysShow(true);
                router.setRedirect("noRedirect");
                router.setChildren(buildMenus(cMenus));
            } else if (isMenuFrame(menu)) {
                List<RouterVo> childrenList = new ArrayList<>();
                RouterVo children = new RouterVo();
                children.setPath(menu.getPath());
                children.setComponent(menu.getComponent());
                children.setName(StringUtils.capitalize(menu.getPath()));
                children.setMeta(new MetaVo(menu.getTitle(), menu.getIcon()));
                childrenList.add(children);
                router.setChildren(childrenList);
            }
            routers.add(router);
        }
        return routers;
    }

    /**
     * 获取路由名称
     *
     * @param menu 菜单信息
     * @return 路由名称
     */
    public String getRouteName(SysMenuNode menu) {
        String routerName = StringUtils.capitalize(menu.getPath());
        // 非外链并且是一级目录（类型为目录）
        if (isMenuFrame(menu)) {
            routerName = StringUtils.EMPTY;
        }
        return routerName;
    }

    /**
     * 获取路由地址
     *
     * @param menu 菜单信息
     * @return 路由地址
     */
    public String getRouterPath(SysMenuNode menu) {
        String routerPath = menu.getPath();
        // 非外链并且是一级目录（类型为目录）
        if (0 == menu.getParentId().intValue()
                && menu.getType() == 0
                && menu.getFrameFlag() == 0) {
            routerPath = "/" + menu.getPath();
        }
        // 非外链并且是一级目录（类型为菜单）
        else if (isMenuFrame(menu)) {
            routerPath = "/";
        }
        return routerPath;
    }

    /**
     * 获取组件信息
     *
     * @param menu 菜单信息
     * @return 组件信息
     */
    public String getComponent(SysMenuNode menu) {
        String component = "Layout";
        if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {
            component = menu.getComponent();
        }
        return component;
    }

    /**
     * 是否为菜单内部跳转
     *
     * @param menu 菜单信息
     * @return 结果
     */
    public boolean isMenuFrame(SysMenuNode menu) {
        return menu.getParentId().intValue() == 0
                && menu.getType() == 1
                && menu.getFrameFlag() == 0;
    }

    /**
     * 根据父节点的ID获取所有子节点
     *
     * @param list     分类表
     * @param parentId 传入的父节点ID
     * @return String
     */
    public List<SysMenuNode> getChildPerms(List<SysMenuNode> list, int parentId) {
        List<SysMenuNode> returnList = new ArrayList<>();
        for (SysMenuNode t : list) {
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (t.getParentId() == parentId) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        return returnList;
    }

    /**
     * 递归列表
     */
    private void recursionFn(List<SysMenuNode> list, SysMenuNode t) {
        // 得到子节点列表
        List<SysMenuNode> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SysMenuNode tChild : childList) {
            if (hasChild(list, tChild)) {
                // 判断是否有子节点
                Iterator<SysMenuNode> it = childList.iterator();
                while (it.hasNext()) {
                    SysMenuNode n = it.next();
                    recursionFn(list, n);
                }
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<SysMenuNode> getChildList(List<SysMenuNode> list, SysMenuNode t) {
        List<SysMenuNode> childList = new ArrayList<>();
        Iterator<SysMenuNode> it = list.iterator();
        while (it.hasNext()) {
            SysMenuNode n = it.next();
            if (n.getParentId().longValue() == t.getId().longValue()) {
                childList.add(n);
            }
        }
        return childList;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SysMenuNode> list, SysMenuNode t) {
        return CollectionUtils.isNotEmpty(getChildList(list, t));
    }

    /**
     * 将SysMenu转化为SysMenuNode并设置children属性
     */
    private SysMenuNode covertMenuNode(SysMenu menu, List<SysMenu> menuList) {
        SysMenuNode node = new SysMenuNode();
        BeanUtils.copyProperties(menu, node);
        List<SysMenuNode> children = menuList.stream()
                .filter(subMenu -> subMenu.getParentId().equals(menu.getId()))
                .map(subMenu -> covertMenuNode(subMenu, menuList)).collect(Collectors.toList());
        node.setChildren(children);
        return node;
    }

}
